# Confidentiality Notice: Please note that you are accessing a confidential
# proprietary script which is owned by Synopsys, Inc. and considered part of
# the PrimeTime software. Access is only permitted under the terms of the
# International Symposium on Physical Design 2012 Contest PrimeTime
# Non-Disclosure Agreement.

proc startup_error_quit {} {
    # Error occured, inform sizer
    exec touch "__PT_STARTUP_ERROR_"
    quit
}

proc ispd_read_design {design_name} {
    set lib_name "contest"
    set ::search_path [list . $::env(ISPD_CONTEST_ROOT)/lib]
    set ::link_library [list $lib_name]
    if { 0 == [read_lib $::env(ISPD_CONTEST_ROOT)/lib/$lib_name.lib] } {
        return "err"
    } else {
        puts "-Info-: Read contest library"
    }
    
    remove_design -all
    
    if { 0 == [read_verilog $::env(ISPD_CONTEST_ROOT)/$design_name/$design_name.v] } {
        return "err"
    } else {
        puts "-Info-: Read verilog"
    }
    link
    
    if { 0 == [read_sdc $::env(ISPD_CONTEST_ROOT)/$design_name/$design_name.sdc] } {
        return "err"        
    } else {
        puts "-Info-: Read SDC"
    }
    if { 0 == [read_parasitics $::env(ISPD_CONTEST_ROOT)/$design_name/$design_name.spef] } {
        return "err"
    } else {
        puts "-Info-: Read SPEF"
    }

    return ""
}

proc run_timing {} {
    set desName [get_attribute [get_design] full_name]
    
    if {$desName == ""} {
        puts "Error: Design not loaded!"
        return "err"
    }
    
    set timingFileName "$desName/$desName.timing"
    file delete -force $timingFileName

    #################################################
    ######## Set cell sizes                      ####
    #################################################

    set sizeFileName "$desName/$desName.int.sizes"
    
    if {[catch {set ifp [open $sizeFileName r]}]} {

        puts "Error: $sizeFileName could not be opened for reading"
        return "err"
    }
    
    while {[gets $ifp line] >= 0} {

        if {[regexp {(\S+)[\s\t]+(\S+)} $line dummy cellName newSizeName]} {
            
            set cell [get_cell $cellName] 
            if {$cell == ""} {
                puts "Error: Invalid cell $cellName"
                return "err"
            }

            set oldSizeName [get_attribute $cell ref_name]
            if {$oldSizeName != $newSizeName} {
                # Change size only if it's different than the original
                if {[catch {size_cell $cell $newSizeName}]} {
                    puts "Error: Could not change the size of $cellName from $origSizeName to $newSizeName"
                    return "err"
                }
            }
        }
    }

    close $ifp


    #################################################
    ######## Update timing                       ####
    #################################################    

    set ::timing_save_pin_arrival_and_slack true
    update_timing

    
    #################################################
    ######## Write timing info                   ####
    #################################################    

    if {[catch {set ofp [open $timingFileName w]}]} {
        puts "Error: Cannot open file $timingFileName for write"
        return "err"
    }

    puts $ofp "# pin timing\n"
    foreach_in_collection pin [get_pins *] {
        if { [get_attribute $pin is_clock_pin] } {
            # don't print timing for clock pins
            continue
        }
        set pinName [get_attribute $pin full_name]
        #set riseArrival [get_attribute $pin max_rise_arrival]
        #set fallArrival [get_attribute $pin max_fall_arrival]
        set riseSlack [get_attribute $pin max_rise_slack]
        set fallSlack [get_attribute $pin max_fall_slack]
        set riseTransition [get_attribute $pin actual_rise_transition_max]
        set fallTransition [get_attribute $pin actual_fall_transition_max]
        
        puts $ofp "$pinName $riseSlack $fallSlack $riseTransition $fallTransition"
        #puts $ofp "$pinName $riseArrival $fallArrival $riseSlack $fallSlack $riseTransition $fallTransition"
    }

    puts $ofp "\n# port timing\n"
    foreach_in_collection port [get_ports *] {
        set portName [get_attribute $port full_name]
        if { "ispd_clk" == $portName } {
            # Clock ports will always have this name in ISPD 2012 benchmarks
            continue;
        }
        #set riseArrival [get_attribute $port endpoint_output_delay_value]
        #set fallArrival [get_attribute $port endpoint_output_delay_value]
        set riseSlack [get_attribute $port max_rise_slack]
        set fallSlack [get_attribute $port max_fall_slack]
        set riseTransition [get_attribute $port actual_rise_transition_max]
        set fallTransition [get_attribute $port actual_fall_transition_max]
        
        puts $ofp "$portName $riseSlack $fallSlack $riseTransition $fallTransition"
        #puts $ofp "$portName $riseArrival $fallArrival $riseSlack $fallSlack $riseTransition $fallTransition"
    }    

    close $ofp
    
    return "success"
}

proc get_files { dir } {
    set currDir [ pwd ]
    cd $dir
    set files [ glob -nocomplain "__TCMD_*" ]
    cd $currDir
    return $files
}

proc get_timer_command { files } {
    if { -1 != [ lsearch $files "__TCMD_RUNTIMER_" ] } {
        return "__TCMD_RUNTIMER_"
    }
    if { -1 != [ lsearch $files "__TCMD_SHUTDOWN_" ] } {
        return "__TCMD_SHUTDOWN_"
    }
    return ""
}

proc timer_loop { } {
    set desName [get_attribute [get_design] full_name]
    #puts "WARNING - CHANGE DESIGN NAME in timer_loop"
    #set desName "adder"

    if {$desName == ""} {
        puts "Error: Design not loaded!"
        return "err"
    }

    puts "Design is $desName"

    set wt 2
    set done 0
    set dir "$::env(ISPD_CONTEST_ROOT)/$desName"
    puts "Directory is $desName"
    set i 1
    while { !$done } { 
        # Get a command
        set ptcmd [get_timer_command [ get_files $dir ] ]
        incr i
        if { "" == $ptcmd } {
            # wait for $wt sec and repeat
            # puts "Timer: Waiting $wt seconds"
            exec sleep $wt
            continue
        }
        puts "-I- Timer: Received command $i: '$ptcmd'"
        set r ""
        if { "__TCMD_RUNTIMER_" == $ptcmd } {
            # Run timing analysis
            file delete -force "$dir/__SIZERCMD_TIMERERROR_"
            file delete -force "$dir/__SiZERCMD_TIMERDONE_"    
            set r [ run_timing ]
            # Erase timer command
            set filename "$dir/$ptcmd"
            file delete -force $filename            
            # Notify sizer of status
            if { "err" == $r } {
                # Error occured, inform sizer
                exec touch "$dir/__SIZERCMD_TIMERERROR_"
            } else {
                exec touch "$dir/__SIZERCMD_TIMERDONE_"
            }
        } elseif { "__TCMD_SHUTDOWN_" == $ptcmd } {
            # MUST NOT ERASE TIMER SHUTDOWN COMMAND (timer.tcl must do it)
            puts "-I- Timer: Synopsys PrimeTime exiting"
            quit
        } else {
            set r ""
            puts "-E- Timer: Invalid command '$ptcmd', ignoring it"
            exec sleep $wt
        }    
    }
}

#--------------------------------------------------------
# Suppress resizing messages
suppress_message "NED-045"

#--------------------------------------------------------
# Perform checks on required environment variable
if { [catch {set contest_root $::env(ISPD_CONTEST_ROOT)} ] } {
    puts "Error: Please define environment variable ISPD_CONTEST_ROOT and re-run pt_shell.tcl"
    startup_error_quit
}
if { [catch {set design_name $::env(ISPD_CONTEST_BENCHMARK)} ] } {
    puts "Error: Please define environment variable ISPD_CONTEST_BENCHMARK and re-run pt_shell.tcl"
    startup_error_quit
}

puts "Contest root: '$contest_root'"
puts "Design name:  '$design_name'"

set design_read_status [ispd_read_design $design_name]
if { "err" == $design_read_status } {
    puts "Error: Could not read design successfully"
    startup_error_quit
}

# Enter the timer loop
timer_loop
